/*
 * MIT License
 *
 * Copyright (c) 2024-2048 冰羽
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package cn.star.framework.dispatch.web.service.impl;

import cn.star.framework.dispatch.web.service.PlatformService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.xxl.adapter.core.XxlConvert;
import com.xxl.adapter.entity.JobGroup;
import com.xxl.adapter.entity.JobInfo;
import com.xxl.adapter.repository.JobGroupRepository;
import com.xxl.adapter.repository.JobInfoRepository;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.core.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.core.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.service.XxlJobService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import java.util.Date;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * PlatformServiceImpl2<br>
 *
 * @author zhaoweiping
 *     <p style='color: red'>Created on 2024-09-23 11:06:00
 * @since 3.0.0
 */
@Slf4j
@Service
public class PlatformServiceImpl implements PlatformService, XxlConvert {

  @Autowired private XxlJobService xxlJobService;
  @Autowired private JobInfoRepository jobInfoRepository;
  @Autowired private JobGroupRepository jobGroupRepository;

  private void checkJobInfoKeyEmpty(String key) {
    Assert.isFalse(StrUtil.isBlank(key), "业务 key 不能为空");
  }

  private void wrapper(ReturnT<String> returnT) {
    if (returnT.getCode() == 500) {
      throw new RuntimeException(returnT.getMsg());
    }
  }

  @Override
  public void jobCreate(JobInfo info) {
    String key = info.getKey(), executorName = info.getExecutorName();
    this.checkJobInfoKeyEmpty(key);
    Assert.isFalse(StrUtil.isBlank(executorName), "执行器名称不能为空");

    JobGroup jobGroup = jobGroupRepository.findByAppname(executorName);

    Assert.isFalse(jobGroup == null, "没有找到 {} 对应的执行器", executorName);

    info.setJobGroup(jobGroup.getId());

    if (StrUtil.isBlank(info.getScheduleConf())) {
      info.setScheduleType(ScheduleTypeEnum.NONE.name());
    }
    if (StrUtil.isBlank(info.getMisfireStrategy())) {
      info.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING.name());
    }
    if (StrUtil.isBlank(info.getExecutorRouteStrategy())) {
      info.setExecutorRouteStrategy(ExecutorRouteStrategyEnum.FIRST.name());
    }
    if (StrUtil.isBlank(info.getExecutorBlockStrategy())) {
      info.setExecutorBlockStrategy(ExecutorBlockStrategyEnum.SERIAL_EXECUTION.name());
    }
    if (info.getUpdateTime() != null) {
      info.setGlueUpdatetime(new Date());
    }

    jobInfoRepository.save(info);
  }

  @Override
  public void jobUpdate(JobInfo info) {
    String key = info.getKey();
    this.checkJobInfoKeyEmpty(key);

    JobInfo entity = jobInfoRepository.findByKey(key);

    Assert.isFalse(entity == null, "{} 对应的Job不存在", key);

    entity.setKey(info.getKey());
    entity.setJobDesc(info.getJobDesc());
    entity.setUpdateTime(new Date());
    entity.setAuthor(info.getAuthor());
    entity.setAlarmEmail(info.getAlarmEmail());
    if (StrUtil.isNotBlank(info.getScheduleConf())) {
      entity.setScheduleType(info.getScheduleType());
      entity.setScheduleConf(info.getScheduleConf());
    } else {
      entity.setScheduleType(ScheduleTypeEnum.NONE.name());
      // java.lang.NullPointerException
      entity.setScheduleConf("");
    }
    if (StrUtil.isNotBlank(info.getMisfireStrategy())) {
      entity.setMisfireStrategy(info.getMisfireStrategy());
    } else {
      entity.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING.name());
    }
    if (StrUtil.isNotBlank(info.getExecutorRouteStrategy())) {
      entity.setExecutorRouteStrategy(info.getExecutorRouteStrategy());
    } else {
      entity.setExecutorRouteStrategy(ExecutorRouteStrategyEnum.FIRST.name());
    }
    entity.setExecutorHandler(info.getExecutorHandler());
    entity.setExecutorParam(info.getExecutorParam());
    if (StrUtil.isNotBlank(info.getExecutorBlockStrategy())) {
      entity.setExecutorBlockStrategy(info.getExecutorBlockStrategy());
    } else {
      entity.setExecutorBlockStrategy(ExecutorBlockStrategyEnum.SERIAL_EXECUTION.name());
    }
    entity.setExecutorTimeout(info.getExecutorTimeout());
    entity.setExecutorFailRetryCount(info.getExecutorFailRetryCount());
    entity.setGlueType(info.getGlueType());
    entity.setGlueSource(info.getGlueSource());
    entity.setGlueRemark(info.getGlueRemark());
    if (info.getUpdateTime() != null) {
      entity.setGlueUpdatetime(info.getGlueUpdatetime());
    } else {
      entity.setGlueUpdatetime(new Date());
    }
    entity.setChildJobId(info.getChildJobId());
    entity.setTriggerStatus(info.getTriggerStatus());
    entity.setTriggerLastTime(info.getTriggerLastTime());
    entity.setTriggerNextTime(info.getTriggerNextTime());

    this.wrapper(xxlJobService.update(this.convert(XxlJobInfo.class, entity)));
  }

  @Override
  public void jobUpset(JobInfo info) {
    String key = info.getKey();
    this.checkJobInfoKeyEmpty(key);
    if (jobInfoRepository.findByKey(key) == null) {
      this.jobCreate(info);
    } else {
      this.jobUpdate(info);
    }
  }

  @Override
  public void jobDelete(String key) {
    this.checkJobInfoKeyEmpty(key);
    JobInfo entity = jobInfoRepository.findByKey(key);
    if (entity != null) {
      this.wrapper(xxlJobService.remove(entity.getId()));
      // 停止任务可能失败，但是如果删除成功则视为成功
      try {
        this.wrapper(xxlJobService.stop(entity.getId()));
      } catch (Exception e) {
        log.error("停止 {} 失败，{}", entity.getJobDesc(), e.getMessage());
      }
    } else {
      log.warn("Key = {} 的Job不存在，可能被提前删除", key);
    }
  }

  @Override
  public void jobStart(String key) {
    this.checkJobInfoKeyEmpty(key);
    JobInfo entity = jobInfoRepository.findByKey(key);
    Assert.isFalse(entity == null, "Key = {} 的Job不存在，启动失败");
    this.wrapper(xxlJobService.start(entity.getId()));
  }

  @Override
  public void jobStop(String key) {
    this.checkJobInfoKeyEmpty(key);
    JobInfo entity = jobInfoRepository.findByKey(key);
    Assert.isFalse(entity == null, "Key = {} 的Job不存在，停止失败");
    this.wrapper(xxlJobService.stop(entity.getId()));
  }

  @Override
  public void broadcast(String jobHandler, String params) {
    List<JobInfo> list =
        jobInfoRepository.findAll(
            (root, query, cb) ->
                cb.and(
                    cb.equal(root.get("executorHandler"), jobHandler),
                    cb.equal(
                        root.get("executorRouteStrategy"),
                        ExecutorRouteStrategyEnum.SHARDING_BROADCAST.name())));

    if (CollectionUtil.isNotEmpty(list)) {
      list.forEach(
          i -> {
            JobGroup jobGroup = jobGroupRepository.findById(i.getJobGroup()).orElse(new JobGroup());
            try {
              this.wrapper(
                  xxlJobService.trigger(
                      null, i.getId(), i.getExecutorParam(), jobGroup.getAddressList()));
            } catch (Exception e) {
              log.error("执行任务 {} 出错，{}", i.getJobDesc(), e.getMessage());
            }
          });
    } else {
      throw new RuntimeException("没有找到 " + jobHandler + " 对应的广播任务");
    }
  }

  @Override
  public Logger getLogger() {
    return log;
  }
}
